package com.zeng.ssm.service.support.trans.scanner;

import com.zeng.ssm.service.support.trans.base.IClientHandler;
import com.zeng.ssm.service.support.trans.base.IServerHandler;
import com.zeng.ssm.service.support.trans.annotation.ClientHandler;
import com.zeng.ssm.service.support.trans.annotation.ServerHandler;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.ScopeMetadata;
import org.springframework.context.annotation.ScopeMetadataResolver;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * 客户端+服务端Handler扫描类
 * <p>
 * 作用：扫描被标注了@ClientHandler和@ServerHandler的类，并加入spring容器（Prototype）
 *
 * @author zengwanlin
 * @date 2022-06-24T16:36
 */
public class TransHandlerScanner extends ClassPathBeanDefinitionScanner {

    /**
     * 别名Set，用于去重校验+启动后实例化所有Handler，防止注入异常
     */
    private Set<String> aliasSet = null;

    public TransHandlerScanner(BeanDefinitionRegistry registry) {
        super(registry);
    }

    public TransHandlerScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
        super(registry, useDefaultFilters);
    }

    public TransHandlerScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment) {
        super(registry, useDefaultFilters, environment);
    }

    /**
     * 扫描Handler
     *
     * @param basePackages
     * @return
     */
    public Set<String> scanHandler(String... basePackages) {
        int count = super.scan(basePackages);
        logger.info("共扫描到[" + count + "]个Handler服务");
        return this.aliasSet;
    }

    @Override
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitionHolderSet = super.doScan(basePackages);
        if (!CollectionUtils.isEmpty(beanDefinitionHolderSet)) {
            processBeanDefinitions(beanDefinitionHolderSet);
        }
        return beanDefinitionHolderSet;
    }

    /**
     * 第一次过滤组件
     *
     * @param metadataReader
     * @return
     */
    @Override
    protected boolean isCandidateComponent(MetadataReader metadataReader) {
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        boolean annotated = annotationMetadata.hasAnnotation(ClientHandler.class.getName()) || annotationMetadata.hasAnnotation(ServerHandler.class.getName());
        if (!annotated) {
            // 没有注解
            return false;
        }
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        try {
            Class<?> clazz = ClassUtils.forName(classMetadata.getClassName(), ClassUtils.getDefaultClassLoader());
            return ClassUtils.isAssignable(IClientHandler.class, clazz) || ClassUtils.isAssignable(IServerHandler.class, clazz);
        } catch (ClassNotFoundException e) {
            throw new BeanCreationException(classMetadata.getClassName(), "过滤Handler组件失败，" + e.getMessage(), e);
        }
    }

    /**
     * 注册beanDefinition后自定义逻辑
     * <p>
     * 注册别名+Handler防重校验
     *
     * @param beanDefinitionHolders
     */
    private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitionHolders) {
        BeanDefinitionRegistry registry = getRegistry();
        ClassLoader classLoader = ClassUtils.getDefaultClassLoader();
        this.aliasSet = new HashSet<>(beanDefinitionHolders.size());
        for (BeanDefinitionHolder holder : beanDefinitionHolders) {
            BeanDefinition beanDefinition = holder.getBeanDefinition();
            if (!beanDefinition.isPrototype() || beanDefinition.isSingleton()) {
                throw new BeanCreationException(holder.getBeanName(), String.format("类[%s]的作用域必须是Prototype", holder.getBeanName()));
            }
            // 注册别名
            ClientHandler ClientHandler = null;
            ServerHandler ServerHandler = null;
            try {
                Class<?> targetClass = ClassUtils.forName(beanDefinition.getBeanClassName(), classLoader);
                ClientHandler = AnnotationUtils.getAnnotation(targetClass, ClientHandler.class);
                ServerHandler = AnnotationUtils.getAnnotation(targetClass, ServerHandler.class);
            } catch (ClassNotFoundException e) {
                throw new BeanCreationException(holder.getBeanName(), "加载注解失败，" + e.getMessage(), e);
            }
            String alias;
            if (ClientHandler != null) {
                alias = TransHandlerFactory.getAlias(ClientHandler.sysCode(), ClientHandler.msgCode());
            } else if (ServerHandler != null) {
                alias = TransHandlerFactory.getAlias(ServerHandler.sysCode(), ServerHandler.msgCode());
            } else {
                throw new BeanCreationException(holder.getBeanName(), String.format("从类[%s]上获取注解[%s]或[%s]为空", beanDefinition.getBeanClassName(), ClientHandler.class.getName(), ServerHandler.class.getName()));
            }
            if (!this.aliasSet.add(alias)) {
                throw new BeanCreationException(holder.getBeanName(), "Handler别名重复，请检查：" + Arrays.toString(registry.getAliases(alias)));
            }
            registry.registerAlias(holder.getBeanName(), alias);
        }
    }

    public static class PrototypeMetadataResolver implements ScopeMetadataResolver {
        @Override
        public ScopeMetadata resolveScopeMetadata(BeanDefinition beanDefinition) {
            ScopeMetadata metadata = new ScopeMetadata();
            metadata.setScopeName(ConfigurableBeanFactory.SCOPE_PROTOTYPE);
            metadata.setScopedProxyMode(ScopedProxyMode.NO);
            return metadata;
        }
    }

}
